package org.zztl.json;

import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

public class JSONFormatter {

    private static final int INDENT = 4;
    private static final String EMPTY = "";
    private String lineSeparator = java.security.AccessController.doPrivileged(
            new sun.security.action.GetPropertyAction("line.separator"));
    private Writer writer = new StringWriter();

    public String format(Object input) {
        if (input instanceof Map) {
            formatObject(0, input, false);
        } else if (input instanceof List) {
            formatArray(0, input, false);
        } else {
            throw new JSONFormatException("input not support");
        }
        return writer.toString();
    }

    private void formatObject(int repeat, Object input, boolean subOfObject) {
        if (subOfObject) {
            outAndNewLine(0, "{");
        } else {
            outAndNewLine(repeat, "{");
        }
        Map map = (Map) input;
        Iterator<Map.Entry> it = map.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry entry = it.next();
            if (entry.getValue() instanceof Map) {
                out(repeat + INDENT, String.valueOf("\"" + entry.getKey() + "\"" + ": "));
                formatObject(repeat + INDENT, entry.getValue(), true);
            } else if (entry.getValue() instanceof List) {
                out(repeat + INDENT, String.valueOf("\"" + entry.getKey() + "\"" + ": "));
                formatArray(repeat + INDENT, entry.getValue(), true);
            } else if (entry.getValue() instanceof String) {
                out(repeat + INDENT, String.valueOf("\"" + entry.getKey() + "\"" + ": " + "\"" + entry.getValue() + "\""));
            } else {
                out(repeat + INDENT, String.valueOf("\"" + entry.getKey() + "\"" + ": " + entry.getValue()));
            }
            if (it.hasNext()) {
                out(0, ",");
            }
            newLine();
        }
        out(repeat, "}");
    }

    private void formatArray(int repeat, Object input, boolean subOfObject) {
        if (subOfObject) {
            outAndNewLine(0, "[");
        } else {
            outAndNewLine(repeat, "[");
        }
        List list = (List) input;
        Iterator it = list.iterator();
        while (it.hasNext()) {
            Object next = it.next();
            if (next instanceof Map) {
                formatObject(repeat + INDENT, next, false);
            } else if (next instanceof List) {
                formatArray(repeat + INDENT, next, false);
            } else if (next instanceof String) {
                out(repeat + INDENT, String.valueOf("\"" + next + "\""));
            } else {
                out(repeat + INDENT, String.valueOf(next));
            }
            if (it.hasNext()) {
                out(0, ",");
            }
            newLine();
        }
        out(repeat, "]");
    }


    private void out(int repeat, String str) {
        try {
            writer.write(repeat(' ', repeat).concat(str));
        } catch (IOException e) {
            throw new JSONFormatException(e);
        }
    }

    private void outAndNewLine(int repeat, String str) {
        try {
            writer.write(repeat(' ', repeat).concat(str));
            newLine();
        } catch (IOException e) {
            throw new JSONFormatException(e);
        }
    }

    private void newLine() {
        try {
            writer.write(lineSeparator);
        } catch (IOException e) {
            throw new JSONFormatException(e);
        }
    }

    private String repeat(final char ch, final int repeat) {
        if (repeat <= 0) {
            return EMPTY;
        }
        final char[] buf = new char[repeat];
        for (int i = repeat - 1; i >= 0; i--) {
            buf[i] = ch;
        }
        return new String(buf);
    }

    @Override
    public String toString() {
        return writer.toString();
    }

}
